use common_uu::{IResult, JsonV};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::fs;
use std::path::Path;

pub struct RequestOption {
    pub method: ReqMethod,
    pub data: JsonV,
    pub headers: HashMap<String, JsonV>,
    pub req_content_type: Option<ReqContentType>,
    pub res_type: Option<ResType>,
}

impl Default for RequestOption {
    fn default() -> Self {
        Self {
            method: ReqMethod::get,
            data: Default::default(),
            headers: Default::default(),
            req_content_type: None,
            res_type: None,
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(serde::Serialize)]
pub enum ReqMethod {
    post,
    get,
}

impl Display for ReqMethod {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", json!(self).to_string())
    }
}

#[allow(non_camel_case_types)]
#[derive(serde::Serialize)]
pub enum ReqContentType {
    #[serde(rename = "application/json;charset=utf-8")]
    json,
    #[serde(rename = "application/text; charset=UTF-8")]
    text,
    #[serde(rename = "application/x-www-form-urlencoded; charset=UTF-8")]
    form,
    #[serde(rename = "application/html; charset=UTF-8")]
    html,
    other(String),
}

impl Display for ReqContentType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let v = match self {
            Self::other(v) => json!(v),
            _ => json!(self),
        };
        write!(f, "{}", v.to_string())
    }
}

#[allow(non_camel_case_types)]
#[derive(serde::Serialize)]
pub enum ResType {
    xml,
    html,
    script,
    json,
    jsonp,
    text,
}

impl Display for ResType {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", json!(self).to_string())
    }
}

pub use common_uu::run_time::retry_async as retry_my;

pub fn not_ascii_handler(mut v: String) -> String {
    for x in v.to_string().chars() {
        if !x.is_ascii() {
            let vv = percent_encoding::percent_encode_byte(x as u8);
            v = v.replace(x, vv);
        }
    }
    v
}

/// 驱动下载
/// download_url: https://cdn.npm.taobao.org/dist/chromedriver/91.0.4472.19/chromedriver_win32.zip
///
pub fn drive_download(download_url: &str, path: impl AsRef<Path>) -> IResult<()> {
    let input_path = path.as_ref();
    let input_name = input_path
        .file_name()
        .and_then(|v| v.to_str())
        .unwrap_or_default();
    let path = std::fs::File::open(input_path).map_err(|e| e.kind());
    match &path {
        Err(std::io::ErrorKind::NotFound) => (),
        _ => return Ok(()),
    }
    debug!("检测到当前没有驱动,开始下载驱动: {}", download_url);
    let zip_file = &format!("chromedriver_win32_{}.zip", input_name);
    common_uu::req::get_file(download_url.to_string(), zip_file)?;
    let f = std::fs::File::open(zip_file)?;
    let mut archive = zip::ZipArchive::new(f).map_err(|e| format!("zip error: {:?}", e))?;
    for i in 0..archive.len() {
        let mut file = archive
            .by_index(i)
            .map_err(|e| format!("zip by_index error: {:?}", e))?;
        let mut outpath = match file.enclosed_name() {
            Some(path) => path.to_owned(),
            None => continue,
        };
        if outpath.extension().and_then(|v| v.to_str()) == Some("exe") {
            outpath = input_path.to_path_buf();
        }
        println!(
            "\"{}\" File ({} bytes) is Downloaded",
            outpath.display(),
            file.size()
        );
        if let Some(p) = outpath.parent() {
            if !p.exists() {
                fs::create_dir_all(&p).unwrap();
            }
        }
        let mut outfile = fs::File::create(&outpath)?;
        std::io::copy(&mut file, &mut outfile)?;
    }
    std::fs::remove_file(zip_file)?;
    Ok(())
}
